//==================================================================
//  Copyright (C) 2006  Davide Pasca
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//==================================================================
///
///
///
///
///
//==================================================================

#include "stdafx.h"
#include "read3ds.h"

//==================================================================
using namespace	M5D;
using namespace PMath;

//===================================================
static inline u_int diff_sign( float a, float b )
{
	//return (a < 0 && b > 0) || (a > 0 && b < 0);

	return (*((u_int *)&a) ^ *((u_int *)&b)) & 0x80000000;
}

//===================================================
static inline bool is_point_in_poly( const float *pt, const float *v0, const float *v1, const float *v2, int nbias )
{
	float	newside, prevside;
	int		i, j;

	switch ( nbias )
	{
	case 0:	i = 1, j = 2;	break;	// check on yz plane
	case 1:	i = 2, j = 0;	break;	// check on zx plane
	case 2: i = 0, j = 1;	break;	// check on xy plane
	}

	newside = (pt[i]-v1[i]) * (v0[j]-v1[j]) - (v0[i]-v1[i]) * (pt[j]-v1[j]);
	if ( newside == 0 )	return false;
	prevside = newside;

	newside = (pt[i]-v2[i]) * (v1[j]-v2[j]) - (v1[i]-v2[i]) * (pt[j]-v2[j]);
	if ( newside == 0 || diff_sign( newside, prevside ) )
		return false;
	prevside = newside;

	newside = (pt[i]-v0[i]) * (v2[j]-v0[j]) - (v2[i]-v0[i]) * (pt[j]-v0[j]);
	if ( newside == 0 || diff_sign( newside, prevside ) )
		return false;

	return true;
}

//==================================================================
static __inline int get_normal_bias( const Vector3 &n )
{
	if ( fabs(n._x) > fabs(n._y) )
	{
		if ( fabs(n._x) > fabs(n._z) )
			return 0;
		else
			return 2;
	}
	else
	{
		if ( fabs(n._y) > fabs(n._z) )
			return 1;
		else
			return 2;
	}
}

//==================================================================
static int count_ray_intersections( Mesh *meshp, const Vector3 &ray_dir, const Vector3 &ray_start, const Triangle *skip_tp )
{
	int	n_intersections = 0;

	MultiVerts		&verts = meshp->GetVerts();
	const Vector3	*posp = verts.GetPosR();

	M5D::triangle_iter	end = meshp->GetTrigs()._list.end();
	for (M5D::triangle_iter tp=meshp->GetTrigs()._list.begin(); tp != end; ++tp)
	{
		float	t1, t2, t;

		if ( &tp[0] == skip_tp )
			continue;

		// inters = ray_start + t * ray_dir
		// plane: inters * n + d = 0

		// (ray_start + t * ray_dir) * n + d = 0
		// ray_start + t * ray_dir = -d / n
		// ray_start/ray_dir + t = -d / n / ray_dir
		// t = -ray_start/ray_dir -d / n / ray_dir 
		// t = -ray_start/ray_dir - d / (n * ray_dir)
		// t = (-ray_start - d / n) / ray_dir
		// t = -(ray_start*n + d) / (ray_dir * n)

		const Vector3	&tn = tp->tplane.GetVector3();

		t2 = ray_dir.GetDot( tn );

		if NOT( t2 > -0.00001f && t2 < 0.00001f )
		{
			float	oo_t2 = 1.0f / t2;

			t1 = ray_start.GetDot( tn ) * -1.0f;
			t = t1 * oo_t2;
			if ( t > 0.00001f )//&& t <= -0.00001f )
			{
				const Vector3 &v0 = posp[ tp->pos[0] ];
				const Vector3 &v1 = posp[ tp->pos[1] ];
				const Vector3 &v2 = posp[ tp->pos[2] ];

				Vector3	inters_point = ray_start + ray_dir * t;

				if ( is_point_in_poly(
					(const float *)inters_point._v,
					(const float *)v0._v,
					(const float *)v1._v,
					(const float *)v2._v,
					get_normal_bias( tn ) ) )
				{
					n_intersections += 1;
				}
			}
		}
	}

	return n_intersections;
}

//==================================================================
static int adjust_faces_orientation( Mesh *meshp )
{
	int adjusted_faces = 0;

	MultiVerts		&verts = meshp->GetVerts();
	const Vector3	*posp = verts.GetPosR();

	for (M5D::triangle_iter tp=meshp->GetTrigs()._list.begin(); tp != meshp->GetTrigs()._list.end(); ++tp)
	{
		const Vector3	&v0 = posp[ tp->pos[0] ];
		const Vector3	&v1 = posp[ tp->pos[1] ];
		const Vector3	&v2 = posp[ tp->pos[2] ];

		Vector3	center = (v0 + v1 + v2) * (1.0f/3);

		int	n_intersections = count_ray_intersections( meshp, tp->tplane.GetVector3(), center, &tp[0] );
		if ( n_intersections )//|| (n_intersections & 1) )
		{
			int t = tp->pos[1];
			tp->pos[1] = tp->pos[2];
			tp->pos[2] = t;

			tp->tplane = -tp->tplane;

			adjusted_faces += 1;
		}
	}

	return adjusted_faces;
}

//###############################################################################################
//###############################################################################################
//###############################################################################################
//###############################################################################################

#define USE_QUAT

enum {
M3DMAGIC          = 0x4D4D,
COLOR_F           = 0x0010,
COLOR_24          = 0x0011,
INT_PERCENTAGE    = 0x0030,
FLOAT_PERCENTAGE  = 0x0031,
MASTER_SCALE      = 0x0100,
MDATA             = 0x3D3D,
AMBIENT_LIGHT     = 0x2100,
SOLID_BGND        = 0x1200,
USE_SOLID_BGND    = 0x1201,
FOG               = 0x2200,
FOG_BGND          = 0x2210,
USE_FOG           = 0x2201,
DEFAULT_VIEW      = 0x3000,
VIEW_CAMERA       = 0x3080,
MAT_ENTRY         = 0xAFFF,
NAMED_OBJECT      = 0x4000,
N_TRI_OBJECT      = 0x4100,
N_DIRECT_LIGHT    = 0x4600,
N_CAMERA          = 0x4700,
OBJ_HIDDEN        = 0x4010,
OBJ_DOESNT_CAST   = 0x4012,
POINT_ARRAY       = 0x4110,
FACE_ARRAY        = 0x4120,
MSH_MAT_GROUP     = 0x4130,
TEX_VERTS		  = 0x4140,
SMOOTH_GROUP      = 0x4150,
MESH_MATRIX       = 0x4160,
DL_OFF            = 0x4620,
DL_SPOTLIGHT      = 0x4610,
DL_SHADOWED       = 0x4630,

MAT_NAME          = 0xA000,
MAT_AMBIENT       = 0xA010,
MAT_DIFFUSE       = 0xA020,
MAT_SPECULAR      = 0xA030,
MAT_SHININESS     = 0xA040,
MAT_TRANSPARENCY  = 0xA050,
MAT_TRANSPARENCY_ADD = 0xA083,
MAT_TWO_SIDE	  = 0xA081,
MAT_TEXMAP		  = 0xA200,
MAT_MAPNAME		  = 0xA300,
MAT_MAP_U_SCALE	  = 0xA354,
MAT_MAP_V_SCALE	  = 0xA356,
MAT_MAP_U_OFFSET  = 0xA358,
MAT_MAP_V_OFFSET  = 0xA35A,
MAT_MAP_ROTANGLE  = 0xA35C,

KFDATA			= 0xB000,
OBJECT_NODE_TAG	= 0xB002,
KFSEG			= 0xB008,
KFHDR			= 0xB00A,
NODE_HDR		= 0xB010,
PIVOT			= 0xB013,
POS_TRACK_TAG	= 0xB020,
ROT_TRACK_TAG	= 0xB021,
SCA_TRACK_TAG	= 0xB022,
NODE_ID			= 0xB030
};

#define PUTS		puts

#define EPSILON		0.000001f

//==================================================================
char *Read3DS::read_string( char *s, int max )
{
	char	ch;
	char	*s2;

	s2 = s;
	while ( ch = (char)_memfp->ReadUChar() )
	{
		if ( --max <= 1 )	// one char left.. break so that it will be 0
			break;

		*s2++ = ch;
	}
	*s2++ = 0;

	return s;
}

//==================================================================
inline float Read3DS::read_float()
{
	float	t = _memfp->ReadFloat();
	PSYS_ENDIAN_FIX_L_FLOAT( t );
	return t;
}

//==================================================================
inline void Read3DS::read_float3( float *dp )
{
	dp[0] = read_float();
	dp[1] = read_float();
	dp[2] = read_float();
}

//==================================================================
inline void Read3DS::read_xzy( float *dp )
{
	dp[0] =  read_float();
	dp[2] = -read_float();
	dp[1] =  read_float();
}

//==================================================================
inline int Read3DS::read_int()
{
	int	t = _memfp->ReadInt();
	PSYS_ENDIAN_FIX_L_INT( t );
	return t;
}
//==================================================================
inline u_short Read3DS::read_ushort(void)
{
	u_short	t = _memfp->ReadUShort();
	PSYS_ENDIAN_FIX_L_USHORT( t );
	return t;
}

//==================================================================
void Read3DS::chunk_begin( chunk *c )
{
	c->start = _memfp->GetCurPos();
	c->tag = read_ushort();
	c->length = read_int();
	c->end = c->start + c->length;
}

//==================================================================
void Read3DS::chunk_end( chunk *c )
{
	int step = c->end - _memfp->GetCurPos();
	if( c->end > _gv_chunk )
	    return;
		
	_memfp->SeekFromCurPos( step );
}

//==================================================================
//==================================================================
//==================================================================
void Read3DS::parse_colour( Vector3 &col )
{
	chunk	c;
	chunk_begin( &c );
		switch (c.tag)
		{
		case COLOR_F:	col._v[0] = read_float();
						col._v[1] = read_float();
						col._v[2] = read_float();
						break;
	
		case COLOR_24:	col._v[0] = (float)_memfp->ReadUChar() / 255.0f;
						col._v[1] = (float)_memfp->ReadUChar() / 255.0f;
						col._v[2] = (float)_memfp->ReadUChar() / 255.0f;
						break;

		default:		throw;
						break;
		}
	chunk_end( &c );
}

//==================================================================
float Read3DS::parse_percentage(void)
{
chunk	c;
float	percent = 0.0;

	chunk_begin( &c );
		switch ( c.tag )
		{
		case 0x0030: percent = 255.0f * read_ushort() / 100.0f;	break;
		case 0x0031: percent = 255.0f * read_float();			break;
		}
	chunk_end( &c );

	return percent;
}

//==================================================================
void Read3DS::parse_msh_mat_group( Triangle	*trigsp ) throw(...)
{
	char	name[256];

	read_string( name, sizeof(name) );

	M5D::Material *matep = _scenep->FindMaterial( name );
	PASSERT( matep != NULL );

	for (int i=read_ushort(); i > 0; --i)
	{
		int	face = read_ushort();

		trigsp[ face ].materialp = matep;
    }
}

/*
//==================================================================
static void free_groups( kstream_t *sp )
{
	kstream_free( sp );
}
//==================================================================
static int list_groups( m5d_object_c *op, kstream_t *sp )
{
int		i, j, n, m, g, og, *datap;

	if ( _trigs_n <= 0 )
		return -1;

	kstream_init( sp, sizeof(int) );

	og = -1;
	n = _cur_gp_n_faces;
	for (i=0; i < n; ++i)
	{
		g = _cur_gp_n_facesp[i].smooth_group;
		if ( g != og || i == 0 )
		{
			m = sp->n;
			datap = (int *)sp->datap;
			for (j=0; j < m; ++j)
				if ( datap[j] == g )
					break;

			if ( j == m )
			{
				if NOT( datap = kstream_item_alloc( sp ) )
				{
					kstream_free( sp );
					return -1;
				}
				*datap = g;
			}
		}
		og = g;
	}

	return 0;
}

*/
//==================================================================
/*
static int parse_smooth_group(void)
{
int	i;
//int	group;

//@@@	_cur_obj->smooth_group_used_mask = 0;

	//n = read_ushort();
	for (i=0; i < _cur_gp_n_faces; ++i)
	{
		_cur_gp_facesp[ i ].smooth_group_mask = read_int();

//@@@		_cur_obj->trigsp[i].smooth_group = group;
//@@@		_cur_obj->smooth_group_used_mask |= group;
    }

	return 0;
}
*/

//==================================================================
void Read3DS::parse_face_array( chunk *fc )
{
	chunk	ck;
	int		flags;

	int n_faces = read_ushort();

	M5D::Triangles &trigs = _cur_meshp->GetTrigs();
	trigs.LockW( __FILE__, __LINE__ );

	Triangle	*trigslistp = trigs.AddTrigs( n_faces );

	bool	has_tex_verts = (_cur_meshp->GetVerts().GetTexN() > 0);
	bool	has_col_verts = (_cur_meshp->GetVerts().GetColN() > 0);

	for (int i=0; i < n_faces; ++i)
	{
		u_int	a = read_ushort();
		u_int	b = read_ushort();
		u_int	c = read_ushort();

		// better be safe than a sorry idiot !
		trigslistp[i].pos[0] = a;
		trigslistp[i].pos[1] = c;
		trigslistp[i].pos[2] = b;

		if ( has_tex_verts )
		{
			trigslistp[i].tex[0] = a;
			trigslistp[i].tex[1] = c;
			trigslistp[i].tex[2] = b;
		}

		if ( has_col_verts )
		{
			trigslistp[i].col[0] = a;
			trigslistp[i].col[1] = c;
			trigslistp[i].col[2] = b;
		}

		flags = read_ushort();
	}

	// hack ! (return safely ? not sure what it was...)
	if ( _memfp->GetCurPos() >= _gv_chunk )
	{
		trigs.Unlock();
		return;
	}

	do{
		chunk_begin( &ck );
		if (ck.end <= fc->end && ck.length > 6)
		{
			switch (ck.tag)
			{
			case MSH_MAT_GROUP:
				parse_msh_mat_group( trigslistp );
				break;

			case SMOOTH_GROUP:
//				parse_smooth_group();
				break;
			}
		}
		if( ck.tag != 0 )
			chunk_end( &ck );
		else
		{
			trigs.Unlock();
			throw "Unknown Tag";
		}

	}while ( ck.end < fc->end );

	trigs.Unlock();
}

//==================================================================
void Read3DS::parse_point_array()
{
	int n = read_ushort();
	if ( n == 0 )
	{
		PASSERT( 0 );
		return;
	}

	MultiVerts	&verts = _cur_meshp->GetVerts();

	verts.LockW( __FILE__, __LINE__ );
	Vector3	*posp = verts.AddPosN( n );

	for (int i = 0; i < n; ++i)
	{
		float x = read_float();
		float z = -read_float();
		float y = read_float();

		*posp++ = Vector3( x, y, z );
	}
	verts.Unlock();
}

//==================================================================
void Read3DS::parse_tex_verts()
{
	int n = read_ushort();
	if ( n == 0 )
	{
		PASSERT( 0 );
		return;
	}

	MultiVerts	&verts = _cur_meshp->GetVerts();

	verts.LockW( __FILE__, __LINE__ );
	Vector2	*texp = verts.AddTexN( n );

	for (int i = 0; i < n; ++i)
	{
		float s =     read_float();
		float t = 1 - read_float();

		*texp++ = Vector2( s, t );
	}
	verts.Unlock();
}

//==================================================================
/*
void assing_tex_verts_to_faces( obj3d obj )
{
int			i, ii;
int			a, b, c;

	if ( _tex_verts == null )
		return;

	for (i=0; i < obj.tp->trigs_n; ++i)
	{
		ii = i * obj3d.TRISIZE;
		a = obj._trigs[ ii + 0 ];
		b = obj._trigs[ ii + 3 ];
		c = obj._trigs[ ii + 6 ];

		obj._trigs[ ii + 1 ] = (int)(_tex_verts[ a * 2 + 0 ] * 1024);
		obj._trigs[ ii + 2 ] = (int)(_tex_verts[ a * 2 + 1 ] * 1024);
		obj._trigs[ ii + 4 ] = (int)(_tex_verts[ b * 2 + 0 ] * 1024);
		obj._trigs[ ii + 5 ] = (int)(_tex_verts[ b * 2 + 1 ] * 1024);
		obj._trigs[ ii + 7 ] = (int)(_tex_verts[ c * 2 + 0 ] * 1024);
		obj._trigs[ ii + 8 ] = (int)(_tex_verts[ c * 2 + 1 ] * 1024);
	}
}
*/

//==================================================================
void Read3DS::parse_mesh_matrix()
{
	float	mat3ds[4][3];

	for (int i=0; i < 4; ++i)
	{
		for (int j=0; j < 3; ++j)
		{
			mat3ds[i][j] = read_float();
		}
	}

	Matrix44	tmat_rot, tmat_tra;

	tmat_rot.Identity();

	tmat_rot._m11 = mat3ds[0][0];
	tmat_rot._m12 = mat3ds[0][1];
	tmat_rot._m13 = mat3ds[0][2];
			
	tmat_rot._m21 = mat3ds[1][0];
	tmat_rot._m22 = mat3ds[1][1];
	tmat_rot._m23 = mat3ds[1][2];
			
	tmat_rot._m31 = mat3ds[2][0];
	tmat_rot._m32 = mat3ds[2][1];
	tmat_rot._m33 = mat3ds[2][2];

	tmat_tra.Identity();
	tmat_tra._m41 = mat3ds[3][0];
	tmat_tra._m42 = mat3ds[3][1];
	tmat_tra._m43 = mat3ds[3][2];

	//read_xzy( tmat._r[0] );
	//read_xzy( tmat._r[1] );
	//read_xzy( tmat._r[2] );
	//read_xzy( tmat._r[3] );

	//tmat.BackTransform();

	tmat_rot.Identity();
	tmat_tra.Identity();
	_cur_meshp->SetMatrix( tmat_tra * tmat_rot );
}

//==================================================================
void Read3DS::parse_n_tri_object( chunk *fc ) throw(...)
{
	chunk	c;

	do{
		chunk_begin( &c );
		if ( c.end <= fc->end && c.length > 6 )
		{
			switch ( c.tag )
			{
			case POINT_ARRAY:	parse_point_array();	break;
			case TEX_VERTS:		parse_tex_verts();		break;
			case FACE_ARRAY:	parse_face_array( &c );	break;
			case MESH_MATRIX:	parse_mesh_matrix();	break;
			}
		}

		if( c.tag == 0 )
			throw;

		chunk_end( &c );

	}while ( c.end < fc->end );

	//assing_tex_verts_to_faces( _cur_obj );
}

//==================================================================
void Read3DS::parse_named_object( chunk *fc ) throw(...)
{
	chunk	c;
	char	buff[128];

	read_string( buff, sizeof(buff) );

	do{
		chunk_begin( &c );
		if ( c.end <= fc->end && c.length > 6 )
		{
			switch ( c.tag )
			{
			case N_TRI_OBJECT:
					_cur_meshp = _scenep->NewMesh( buff, _root_modelp );

					parse_n_tri_object( &c );

					//_cur_meshp->OptimizeVerts();
					_cur_meshp->calcTrigsNormals();

//					adjust_faces_orientation( _cur_meshp );
//					_cur_meshp->msh_trigs_normals_calc();

					_cur_meshp->calcVertsNormals( true );
					//_cur_meshp->UnshareVertsByNormalAngle( 45.0f );
					_cur_meshp->CalcBoundingBox();
					break;
					
			//case OBJ_HIDDEN:		if (_meshP)	_meshP->hidden = 1;	break;
			//case OBJ_DOESNT_CAST:	if (_meshP)	_meshP->shadow = 0;	break;
			//case N_DIRECT_LIGHT:	parse_n_direct_light(&chunk);	break;
			//case N_CAMERA:			parse_n_camera();				break;
			}
		}

		if( c.tag != 0 )
			chunk_end( &c );
		else
			throw;

	}while ( c.end < fc->end );
}

//==================================================================
void Read3DS::parse_texmap( chunk *fc, M5D::Material *matep )
{
	chunk			c;
	float			t;
	//M5D::Texture	*texp;
	char			txnamebuff[256];

	do{
		chunk_begin( &c );
		if (c.end <= fc->end && c.length > 6)
		{
			switch (c.tag)
			{
			case MAT_MAPNAME:
				read_string( txnamebuff, sizeof(txnamebuff) );
				//texp = _scenep->NewTexture( txnamebuff, txnamebuff );
				//matep->SetTexture( texp );
				break;

			case MAT_MAP_U_SCALE:	t = read_float();	break;
			case MAT_MAP_V_SCALE:	t = read_float();	break;
			case MAT_MAP_U_OFFSET:	t = read_float();	break;
			case MAT_MAP_V_OFFSET:	t = read_float();	break;
			case MAT_MAP_ROTANGLE:	t = read_float();	break;
			}
		}

		if( c.tag != 0 )
			chunk_end( &c );
		else
			throw;

	}while ( c.end < fc->end );
}

//==================================================================
void Read3DS::parse_mat_entry( chunk *fc )
{
	chunk			c;
	M5D::Material	*matep;
	Vector3			col;
	char			buff[128];

	matep = _scenep->NewMaterial( "3ds mate" );

	do{
		chunk_begin( &c );

		if ( c.end <= fc->end )
		{
			switch ( c.tag )
			{
			case 0xA000:	read_string( buff, sizeof(buff) );
							matep->SetName( buff );
						 //printf( "Material: %s\n", matep->name );
							break;

			case MAT_TEXMAP: parse_texmap( &c, matep );
							 break;

//			case MAT_AMBIENT: parse_colour( col );
//							  matep->m4mt_ambient_setv( col );
//							  break;

			case MAT_DIFFUSE: parse_colour( col );
							  matep->SetDiffuseCol( Vector4( col, 1 ) );
							  break;

			case MAT_SPECULAR: parse_colour( col );
							   matep->SetSpecularCol( col );
							   break;

	//		case 0xA040: mate.shininess = 100.0*parse_percentage();	break;
			case MAT_TRANSPARENCY:
							//matep->_translucency = parse_percentage();
							break;
			
			case MAT_TRANSPARENCY_ADD:
							//matep->translucency_is_add = 1;
							break;

	/*		case 0xA080: mpr_self_illum = TRUE;						break; */
			//case 0xA220: mate.reflection = parse_percentage();	//(void)parse_mapname (&chunk);
			//			 break;

//			case 0xA310: if ( matep->_reflection == 0.0f )
//								matep->_reflection = 1.0f;
//						 break;

	/*		case 0xA200: mpr_tex_strength = parse_percentage();
						 strcpy (mpr_tex_map, parse_mapname (&chunk));	 break;

			case 0xA230: mpr_bump_strength = parse_percentage();
						 strcpy (mpr_bump_map, parse_mapname (&chunk));	 break;
	*/	    }
		}
		chunk_end( &c );

	}while ( c.end < fc->end );
}

//==================================================================
void Read3DS::parse_mdata( chunk *fc ) throw(...)
{
	chunk	c;

	do{
		chunk_begin( &c );

		if ( c.end <= fc->end && c.length > 6 )
		{
			switch (c.tag)
			{
			case 0x2100:
				 //parse_colour( global_amb );
				 break;

			case MAT_ENTRY:
				parse_mat_entry( &c );
				break;
			
			case NAMED_OBJECT:
				parse_named_object( &c );
				break;

			case MASTER_SCALE:
				read_float();
				break;
			}
		}

		if ( c.tag != 0 )
			chunk_end( &c );
		else
			throw;

	}while ( c.end < fc->end );
}

//==================================================================
/* ANIMATION
//==================================================================
static void parse_keyheader( m5d_keyf_head_t *p )
{
	p->time = read_int();
/*	p->spline_bits = read_ushort();

	if ( p->spline_bits & 1  )	p->sp_tension = read_float();
	if ( p->spline_bits & 2  )	p->sp_continuity = read_float();
	if ( p->spline_bits & 4  )	p->sp_bias = read_float();
	if ( p->spline_bits & 8  )	p->sp_easeto = read_float();
	if ( p->spline_bits & 16 )	p->sp_easefrom = read_float();
* /
}

//==================================================================
static void parse_poskeys( m5d_tnode_c *bonep )
{
	for (int i=0; i < bonep->_n_poskeys; ++i)
	{
		parse_keyheader( &bonep->_poskeysp[i].head );
		read_xzy( bonep->_poskeysp[i].pos_vec );
	}
}

//==================================================================
#ifdef USE_QUAT
static void quat_axis_angle( float4 c, float4 axis, float angle )
{
float omega,s;
float l;

	l=sqrt(axis[0]*axis[0] + axis[1]*axis[1] + axis[2]*axis[2]);
	if ( l <EPSILON )
	{
		c[0]=c[1]=c[2]=0.0f;
		c[3]=1.0f;
	}
	else
	{
		omega = -0.5f * angle;
		s = sin(omega) / l;
		c[0] = s*axis[0];
		c[1] = s*axis[1];
		c[2] = s*axis[2];
		c[3] = cos(omega);
	}
}
#endif

//==================================================================
static void parse_rotkeys( m5d_tnode_c *bonep )
{
long	i;
float	angle, axis3ds[3], axis[3];

	for (i=0; i < bonep->_n_rotkeys; ++i)
	{
		parse_keyheader( &bonep->_rotkeysp[i].head );

		angle = read_float();
		read_float3( axis3ds );
		axis[0] =  axis3ds[0];
		axis[1] =  axis3ds[2];
		axis[2] = -axis3ds[1];
#ifdef USE_QUAT
		quat_axis_angle( bonep->_rotkeysp[i].base_q, axis, angle );
#else
		bonep->_rotkeysp[i].rot_vec[3] = angle * 180.0f/3.14159265f;
		bonep->_rotkeysp[i].rot_vec[0] = axis[0];
		bonep->_rotkeysp[i].rot_vec[1] = axis[1];
		bonep->_rotkeysp[i].rot_vec[2] = axis[2];
#endif
		//_keys_rotp[i].axis[0] = -_keys_rotp[i].axis[0];
		//_keys_rotp[i].angle = -_keys_rotp[i].angle;
		//normalize( _keys_rotp[i].axis );
	}
}

//==================================================================
static void parse_scakeys( m5d_tnode_c *bonep )
{
long	i;
float	scale[3];

	for (i=0; i < bonep->_n_scakeys; ++i)
	{
		parse_keyheader( &bonep->_rotkeysp[i].head );
		read_float3( scale );
		bonep->_scakeysp[i].scale[0] =  scale[0];
		bonep->_scakeysp[i].scale[1] =  scale[2];
		bonep->_scakeysp[i].scale[2] =  scale[1]; // - ?
		if ( scale[0] != 1.0f || scale[1] != 1.0f || scale[2] != 1.0f )
			scale[0] = scale[0];
	}
}
*/

//==================================================================
void Read3DS::parse_object_node( chunk *fc )
{
	chunk			c;
	long			err = 0;
	int				node_id = M5D_VOID_INDEX;
	char			name[32];
	int				flag1, flag2;
	int				obj_idx=-1;
	Model			*modelp;

	modelp = NULL;

	do{
		chunk_begin( &c );

		if ( c.end <= fc->end && c.length > 6 )
		{
			switch ( c.tag )
			{
			case NODE_ID:	node_id = read_ushort();
							break;

			case NODE_HDR:	{
								read_string( name, sizeof(name) );

								flag1 = read_ushort();
								flag2 = read_ushort();

								int	parent_id;
								/*
								if ( node_id != 65535 )
									_objectsp[obj_idx].parent_id = node_id;
								else*/
									parent_id = read_ushort();
								
								if ( modelp = _root_modelp->FindByName( name ) )
								{
									//ModelUserData	*userdatap =
									//		new ModelUserData( node_id, parent_id );

									//modelp->_userdatap = userdatap;
								}
							}
							break;

			case PIVOT:		if ( modelp )
							{
								float	pivot[3];
								read_float3( pivot );
								/*modelp->SetMatrix( 
									modelp->GetMatrix() * Matrix44::Tra( pivot[0], pivot[1], pivot[2] )
								);*/
							}
							break;
			/*
			case POS_TRACK_TAG:
				read_ushort();	// flags
				read_int();	// unused
				read_int();	// unused
				bonep = get_bone0( objp );
				
				if ( (n = read_int()) > 0 )
				{
					if NOT( bonep->m4tn_poskeys_addn( n ) )
						return -1;
					parse_poskeys( bonep );
				}
				break;

			case ROT_TRACK_TAG:
				read_ushort();	// flags
				read_int();	// unused
				read_int();	// unused
				bonep = get_bone0( objp );

				if ( (n = read_int()) > 0 )
				{
					if NOT( bonep->m4tn_rotkeys_addn( n ) )
						return -1;
					parse_rotkeys( bonep );
				}
				break;

			case SCA_TRACK_TAG:
				read_ushort();	// flags
				read_int();	// unused
				read_int();	// unused
				bonep = get_bone0( objp );

				if ( (n = read_int()) > 0 )
				{
					if NOT( bonep->m4tn_scakeys_addn( n ) )
						return -1;
					parse_scakeys( bonep );
				}
				break;
			*/
			}
		}

		if( c.tag != 0 )
			chunk_end( &c );
		else
			throw "arghhh !";

	} while ( c.end < fc->end );
}


//==================================================================
void Read3DS::parse_kfdata( chunk *fc )
{
	chunk	c;

	do{
		chunk_begin( &c );

		if ( c.end <= fc->end && c.length > 6 )
		{
			switch ( c.tag )
			{
			//case KFHDR:				err = parse_( &c );	break;
			//case KFSEG:				err = parse_( &c );	break;
			case OBJECT_NODE_TAG:
				parse_object_node( &c );
				break;
			}
		}

		if( c.tag != 0 )
			chunk_end( &c );
		else
			throw "arghhh !";

	}while ( c.end < fc->end );
}

//==================================================================
void Read3DS::parse_3ds( chunk *fc ) throw(...)
{
	chunk	c;

	do {
		chunk_begin( &c );

		if ( c.end <= fc->end && c.length > 6 )
		{
			switch ( c.tag )
			{
			case MDATA:		parse_mdata( &c );	break;
			case KFDATA:	parse_kfdata( &c );	break;
			}
		}

		if( c.tag != 0 )
			chunk_end( &c );
		else
			throw "arghhh !";

	}while ( c.end < fc->end );
}

//==================================================================
Read3DS::Read3DS( M5D::Scene *scenep, PUtils::Memfile &memf, M5D::Model *root_modelp ) :
	_memfp(NULL),
	_gv_chunk(0),
	_scenep(scenep),
	_cur_meshp(NULL),
	_root_modelp(NULL)
{
	if ( root_modelp == NULL )
		_root_modelp = _scenep->GetRootModel();
	else
		_root_modelp = root_modelp;

	_memfp = &memf;
}

//==================================================================
M5D::Mesh *Read3DS::DoRead()
{
	_memfp->SeekFromStart( 0 );

	chunk	c;

	chunk_begin( &c );
	_gv_chunk = c.end;

	if ( c.tag == M3DMAGIC )
		parse_3ds( &c );
	else
		throw;

	return _cur_meshp;
}
